#include "tcp_connection.hh"

#include <iostream>

// Dummy implementation of a TCP connection

// For Lab 4, please replace with a real implementation that passes the
// automated checks run by `make check`.

template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}

using namespace std;

size_t TCPConnection::remaining_outbound_capacity() const { return _sender.stream_in().remaining_capacity(); }

size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); }

size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); }

size_t TCPConnection::time_since_last_segment_received() const {
    return _ms_timestamp - _last_segment_received_timestamp;
}

void TCPConnection::segment_received(const TCPSegment &seg) {
    if (!_active) {
        return;
    }
    // printf("segment_received, seg.payload: %s\n", seg.payload().copy().c_str());
    if (seg.header().rst) {
        // printf("passive close!!\n");
        _active = false;
        _receiver.stream_out().set_error();
        _sender.stream_in().set_error();
        return;
    }
    _last_segment_received_timestamp = _ms_timestamp;
    _receiver.segment_received(seg);
    if (seg.header().ack) {
        _sender.ack_received(seg.header().ackno, seg.header().win);
    }
    _sender.set_receiver_window_size(_receiver.window_size());
    if (_sender.established()) {
        _sender.fill_window();
    }

    // printf("ackno: %d\n", _receiver.ackno()->raw_value());
    if (seg.header().syn || seg.header().fin || !seg.payload().str().empty()) {
        // printf("send ack\n");
        _sender.send_empty_segment(true, _receiver.ackno().value());
    }
    if (_receiver.stream_out().eof() && !_sender.stream_in().eof()) {
        // 对方先释放连接，因此我方释放连接的时候，不需要linger
        //        printf("对方先释放连接，因此我方释放连接的时候，不需要linger\n");
        //        printf("set _linger_after_streams_finish = false;\n");
        _linger_after_streams_finish = false;
    }

    if (_receiver.stream_out().eof() && _sender.stream_in().eof()) {
        if (_sender.bytes_in_flight()) {
            if (_linger_after_streams_finish) {
                // printf("CLOSING!!\n");
            } else {
                //  printf("LAST_ACK!!\n\n");
            }
        } else {
            if (_linger_after_streams_finish) {
                //  printf("TIME_WAIT!!\n");
            } else {
                _active = false;
                //  printf("CLOSED");
            }
        }
        return;
    }
}

bool TCPConnection::active() const { return _active; }

size_t TCPConnection::write(const string &data) { return _sender.stream_in().write(data); }

//! \param[in] ms_since_last_tick number of milliseconds since the last call to this method
void TCPConnection::tick(const size_t ms_since_last_tick) {
    _ms_timestamp += ms_since_last_tick;
    //     printf("ticks\n");
    if (_active && _sender.consecutive_retransmissions() >= TCPConfig::MAX_RETX_ATTEMPTS) {
        _sender.stream_in().set_error();
        _receiver.stream_out().set_error();
        _active = false;
        _sender.send_rst_segment();
        // printf("ticks, active close!!\n");
        return;
    }
    _sender.tick(ms_since_last_tick);
    if (_active && !_sender.stream_in().buffer_empty()) {
        _sender.fill_window();
    }
    if (_sender.bytes_in_flight() == 0 && _sender.stream_in().eof() && _receiver.stream_out().input_ended()) {
        if (_linger_after_streams_finish) {
            if (_ms_timestamp >= _last_segment_received_timestamp + 10 * _cfg.rt_timeout) {
                //printf("tick: clean shutdown\n");
                _active = false;
            }
        }
        if (!_linger_after_streams_finish) {
            //printf("tick: clean shutdown\n");
            _active = false;
        }
    }
}

void TCPConnection::end_input_stream() {
    // printf("close sender stream\n");
    _sender.stream_in().end_input();
    _sender.fill_window();
}

void TCPConnection::connect() {
    // printf("connect\n");
    _sender.fill_window();
    // printf("_sender.bytes_in_flight(): %ld\n", _sender.bytes_in_flight());
}

TCPConnection::~TCPConnection() {
    try {
        if (active()) {
            cerr << "Warning: Unclean shutdown of TCPConnection\n";

            // Your code here: need to send a RST segment to the peer
            // printf("close??\n");
            _sender.stream_in().set_error();
            _receiver.stream_out().set_error();
            _active = false;
            _sender.send_rst_segment();
        }
        // printf("close??\n");
    } catch (const exception &e) {
        std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl;
    }
}
